"""
订单路由器

智能路由订单到最优券商，支持订单拆分和最优执行。
"""

import asyncio
import logging
from datetime import datetime
from decimal import Decimal
from enum import Enum
from typing import Dict, List, Optional, Any, Tuple
from dataclasses import dataclass

from .base_broker import BaseBroker, Order, OrderRequest, OrderSide, OrderType, MarketData
from .broker_manager import BrokerManager


class RoutingStrategy(Enum):
    """路由策略"""
    BEST_PRICE = "best_price"           # 最优价格
    FASTEST_EXECUTION = "fastest_execution"  # 最快执行
    LOWEST_COST = "lowest_cost"         # 最低成本
    VOLUME_WEIGHTED = "volume_weighted" # 成交量加权
    ROUND_ROBIN = "round_robin"         # 轮询
    CUSTOM = "custom"                   # 自定义


@dataclass
class RoutingRule:
    """路由规则"""
    symbol_pattern: str                 # 股票代码模式
    strategy: RoutingStrategy           # 路由策略
    preferred_brokers: List[str]        # 优选券商
    max_order_size: Optional[int] = None # 最大订单大小
    min_order_size: Optional[int] = None # 最小订单大小
    split_threshold: Optional[int] = None # 拆单阈值
    priority: int = 0                   # 优先级（数字越大优先级越高）
    
    def matches(self, symbol: str) -> bool:
        """检查是否匹配股票代码"""
        import re
        try:
            return bool(re.match(self.symbol_pattern, symbol))
        except:
            return symbol == self.symbol_pattern


@dataclass
class BrokerScore:
    """券商评分"""
    broker_id: str
    score: float                        # 综合评分
    price_score: float = 0.0           # 价格评分
    speed_score: float = 0.0           # 速度评分
    cost_score: float = 0.0            # 成本评分
    volume_score: float = 0.0          # 成交量评分
    availability_score: float = 0.0    # 可用性评分
    
    def __lt__(self, other):
        return self.score < other.score


@dataclass
class RoutingResult:
    """路由结果"""
    original_request: OrderRequest
    routed_orders: List[Tuple[str, OrderRequest]]  # (broker_id, order_request)
    strategy_used: RoutingStrategy
    routing_reason: str
    estimated_cost: Decimal
    estimated_execution_time: float
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            'original_request': self.original_request.to_dict(),
            'routed_orders': [
                {'broker_id': broker_id, 'order_request': req.to_dict()}
                for broker_id, req in self.routed_orders
            ],
            'strategy_used': self.strategy_used.value,
            'routing_reason': self.routing_reason,
            'estimated_cost': float(self.estimated_cost),
            'estimated_execution_time': self.estimated_execution_time
        }


class OrderRouter:
    """订单路由器"""
    
    def __init__(self, broker_manager: BrokerManager):
        self.broker_manager = broker_manager
        self.logger = logging.getLogger(__name__)
        
        # 路由规则
        self.routing_rules: List[RoutingRule] = []
        
        # 默认策略
        self.default_strategy = RoutingStrategy.BEST_PRICE
        
        # 券商性能统计
        self.broker_stats: Dict[str, Dict[str, Any]] = {}
        
        # 轮询计数器
        self._round_robin_counter = 0
        
        # 内部状态
        self._lock = asyncio.Lock()
    
    def add_routing_rule(self, rule: RoutingRule):
        """添加路由规则"""
        self.routing_rules.append(rule)
        # 按优先级排序
        self.routing_rules.sort(key=lambda x: x.priority, reverse=True)
        self.logger.info(f"添加路由规则: {rule.symbol_pattern} -> {rule.strategy.value}")
    
    def remove_routing_rule(self, symbol_pattern: str) -> bool:
        """移除路由规则"""
        original_count = len(self.routing_rules)
        self.routing_rules = [rule for rule in self.routing_rules 
                             if rule.symbol_pattern != symbol_pattern]
        
        removed = len(self.routing_rules) < original_count
        if removed:
            self.logger.info(f"移除路由规则: {symbol_pattern}")
        
        return removed
    
    async def route_order(self, order_request: OrderRequest) -> RoutingResult:
        """路由订单"""
        # 查找匹配的路由规则
        matching_rule = self._find_matching_rule(order_request.symbol)
        
        if matching_rule:
            strategy = matching_rule.strategy
            preferred_brokers = matching_rule.preferred_brokers
        else:
            strategy = self.default_strategy
            preferred_brokers = []
        
        # 获取可用券商
        available_brokers = await self._get_available_brokers(preferred_brokers)
        
        if not available_brokers:
            raise RuntimeError("没有可用的券商")
        
        # 根据策略路由订单
        if strategy == RoutingStrategy.BEST_PRICE:
            return await self._route_by_best_price(order_request, available_brokers, matching_rule)
        elif strategy == RoutingStrategy.FASTEST_EXECUTION:
            return await self._route_by_fastest_execution(order_request, available_brokers, matching_rule)
        elif strategy == RoutingStrategy.LOWEST_COST:
            return await self._route_by_lowest_cost(order_request, available_brokers, matching_rule)
        elif strategy == RoutingStrategy.VOLUME_WEIGHTED:
            return await self._route_by_volume_weighted(order_request, available_brokers, matching_rule)
        elif strategy == RoutingStrategy.ROUND_ROBIN:
            return await self._route_by_round_robin(order_request, available_brokers, matching_rule)
        else:
            # 默认使用最优价格策略
            return await self._route_by_best_price(order_request, available_brokers, matching_rule)
    
    def _find_matching_rule(self, symbol: str) -> Optional[RoutingRule]:
        """查找匹配的路由规则"""
        for rule in self.routing_rules:
            if rule.matches(symbol):
                return rule
        return None
    
    async def _get_available_brokers(self, preferred_brokers: List[str]) -> List[str]:
        """获取可用券商列表"""
        all_brokers = list(self.broker_manager.brokers.keys())
        
        # 如果有优选券商，优先使用
        if preferred_brokers:
            available = [broker_id for broker_id in preferred_brokers 
                        if broker_id in all_brokers and 
                        self.broker_manager.brokers[broker_id].is_connected]
            if available:
                return available
        
        # 返回所有可用券商
        return [broker_id for broker_id in all_brokers 
                if self.broker_manager.brokers[broker_id].is_connected]
    
    async def _route_by_best_price(self, order_request: OrderRequest, 
                                  available_brokers: List[str],
                                  rule: Optional[RoutingRule]) -> RoutingResult:
        """按最优价格路由"""
        # 获取各券商的市场数据
        broker_quotes = {}
        for broker_id in available_brokers:
            try:
                market_data = await self.broker_manager.get_market_data(
                    order_request.symbol, broker_id
                )
                if market_data:
                    broker_quotes[broker_id] = market_data
            except Exception as e:
                self.logger.warning(f"获取券商 {broker_id} 市场数据失败: {e}")
        
        if not broker_quotes:
            # 如果没有市场数据，使用第一个可用券商
            best_broker = available_brokers[0]
            reason = "无市场数据，使用默认券商"
        else:
            # 选择最优价格的券商
            if order_request.side == OrderSide.BUY:
                # 买入选择最低卖价
                best_broker = min(broker_quotes.keys(), 
                                key=lambda b: broker_quotes[b].ask_price or Decimal('999999'))
                reason = f"选择最低卖价券商: {broker_quotes[best_broker].ask_price}"
            else:
                # 卖出选择最高买价
                best_broker = max(broker_quotes.keys(), 
                                key=lambda b: broker_quotes[best_broker].bid_price or Decimal('0'))
                reason = f"选择最高买价券商: {broker_quotes[best_broker].bid_price}"
        
        # 检查是否需要拆单
        routed_orders = self._split_order_if_needed(order_request, [best_broker], rule)
        
        return RoutingResult(
            original_request=order_request,
            routed_orders=routed_orders,
            strategy_used=RoutingStrategy.BEST_PRICE,
            routing_reason=reason,
            estimated_cost=self._estimate_cost(order_request, best_broker),
            estimated_execution_time=self._estimate_execution_time(best_broker)
        )
    
    async def _route_by_fastest_execution(self, order_request: OrderRequest,
                                        available_brokers: List[str],
                                        rule: Optional[RoutingRule]) -> RoutingResult:
        """按最快执行路由"""
        # 根据历史统计选择最快的券商
        broker_scores = []
        for broker_id in available_brokers:
            stats = self.broker_stats.get(broker_id, {})
            avg_execution_time = stats.get('avg_execution_time', 1.0)
            
            score = BrokerScore(
                broker_id=broker_id,
                score=1.0 / avg_execution_time,  # 执行时间越短分数越高
                speed_score=1.0 / avg_execution_time
            )
            broker_scores.append(score)
        
        if not broker_scores:
            best_broker = available_brokers[0]
        else:
            best_broker = max(broker_scores).broker_id
        
        routed_orders = self._split_order_if_needed(order_request, [best_broker], rule)
        
        return RoutingResult(
            original_request=order_request,
            routed_orders=routed_orders,
            strategy_used=RoutingStrategy.FASTEST_EXECUTION,
            routing_reason=f"选择最快执行券商: {best_broker}",
            estimated_cost=self._estimate_cost(order_request, best_broker),
            estimated_execution_time=self._estimate_execution_time(best_broker)
        )
    
    async def _route_by_lowest_cost(self, order_request: OrderRequest,
                                   available_brokers: List[str],
                                   rule: Optional[RoutingRule]) -> RoutingResult:
        """按最低成本路由"""
        # 计算各券商的预估成本
        broker_costs = {}
        for broker_id in available_brokers:
            cost = self._estimate_cost(order_request, broker_id)
            broker_costs[broker_id] = cost
        
        best_broker = min(broker_costs.keys(), key=lambda b: broker_costs[b])
        routed_orders = self._split_order_if_needed(order_request, [best_broker], rule)
        
        return RoutingResult(
            original_request=order_request,
            routed_orders=routed_orders,
            strategy_used=RoutingStrategy.LOWEST_COST,
            routing_reason=f"选择最低成本券商: {best_broker}, 成本: {broker_costs[best_broker]}",
            estimated_cost=broker_costs[best_broker],
            estimated_execution_time=self._estimate_execution_time(best_broker)
        )
    
    async def _route_by_volume_weighted(self, order_request: OrderRequest,
                                       available_brokers: List[str],
                                       rule: Optional[RoutingRule]) -> RoutingResult:
        """按成交量加权路由"""
        # 获取各券商的成交量数据
        broker_volumes = {}
        for broker_id in available_brokers:
            try:
                market_data = await self.broker_manager.get_market_data(
                    order_request.symbol, broker_id
                )
                if market_data and market_data.volume:
                    broker_volumes[broker_id] = market_data.volume
                else:
                    broker_volumes[broker_id] = 0
            except Exception:
                broker_volumes[broker_id] = 0
        
        if not any(broker_volumes.values()):
            # 如果没有成交量数据，使用轮询
            return await self._route_by_round_robin(order_request, available_brokers, rule)
        
        # 按成交量比例分配订单
        total_volume = sum(broker_volumes.values())
        routed_orders = []
        
        remaining_quantity = order_request.quantity
        for i, (broker_id, volume) in enumerate(broker_volumes.items()):
            if volume == 0:
                continue
            
            if i == len(broker_volumes) - 1:
                # 最后一个券商分配剩余数量
                quantity = remaining_quantity
            else:
                # 按比例分配
                ratio = volume / total_volume
                quantity = int(order_request.quantity * ratio)
                quantity = min(quantity, remaining_quantity)
            
            if quantity > 0:
                sub_request = OrderRequest(
                    symbol=order_request.symbol,
                    side=order_request.side,
                    order_type=order_request.order_type,
                    quantity=quantity,
                    price=order_request.price,
                    stop_price=order_request.stop_price,
                    time_in_force=order_request.time_in_force,
                    client_order_id=f"{order_request.client_order_id}_{i}" if order_request.client_order_id else None
                )
                routed_orders.append((broker_id, sub_request))
                remaining_quantity -= quantity
        
        return RoutingResult(
            original_request=order_request,
            routed_orders=routed_orders,
            strategy_used=RoutingStrategy.VOLUME_WEIGHTED,
            routing_reason="按成交量加权分配订单",
            estimated_cost=sum(self._estimate_cost(req, broker_id) for broker_id, req in routed_orders) or Decimal('0'),
            estimated_execution_time=max(self._estimate_execution_time(broker_id) for broker_id, _ in routed_orders)
        )
    
    async def _route_by_round_robin(self, order_request: OrderRequest,
                                   available_brokers: List[str],
                                   rule: Optional[RoutingRule]) -> RoutingResult:
        """轮询路由"""
        async with self._lock:
            broker_index = self._round_robin_counter % len(available_brokers)
            self._round_robin_counter += 1
        
        best_broker = available_brokers[broker_index]
        routed_orders = self._split_order_if_needed(order_request, [best_broker], rule)
        
        return RoutingResult(
            original_request=order_request,
            routed_orders=routed_orders,
            strategy_used=RoutingStrategy.ROUND_ROBIN,
            routing_reason=f"轮询选择券商: {best_broker}",
            estimated_cost=self._estimate_cost(order_request, best_broker),
            estimated_execution_time=self._estimate_execution_time(best_broker)
        )
    
    def _split_order_if_needed(self, order_request: OrderRequest, 
                              brokers: List[str],
                              rule: Optional[RoutingRule]) -> List[Tuple[str, OrderRequest]]:
        """根据需要拆分订单"""
        if not rule or not rule.split_threshold or order_request.quantity <= rule.split_threshold:
            # 不需要拆单
            return [(brokers[0], order_request)]
        
        # 需要拆单
        routed_orders = []
        remaining_quantity = order_request.quantity
        broker_index = 0
        
        while remaining_quantity > 0:
            broker_id = brokers[broker_index % len(brokers)]
            
            # 确定这个子订单的数量
            if rule.max_order_size:
                sub_quantity = min(remaining_quantity, rule.max_order_size)
            else:
                sub_quantity = min(remaining_quantity, rule.split_threshold)
            
            # 创建子订单
            sub_request = OrderRequest(
                symbol=order_request.symbol,
                side=order_request.side,
                order_type=order_request.order_type,
                quantity=sub_quantity,
                price=order_request.price,
                stop_price=order_request.stop_price,
                time_in_force=order_request.time_in_force,
                client_order_id=f"{order_request.client_order_id}_{len(routed_orders)}" if order_request.client_order_id else None
            )
            
            routed_orders.append((broker_id, sub_request))
            remaining_quantity -= sub_quantity
            broker_index += 1
        
        return routed_orders
    
    def _estimate_cost(self, order_request: OrderRequest, broker_id: str) -> Decimal:
        """估算交易成本"""
        # 简化的成本估算
        # 实际实现中应该考虑券商的手续费率、印花税等
        
        # 假设价格
        if order_request.price:
            price = order_request.price
        else:
            price = Decimal('100')  # 默认价格
        
        amount = Decimal(str(order_request.quantity)) * price
        
        # 假设手续费率
        commission_rate = Decimal('0.0003')  # 万三
        min_commission = Decimal('5')        # 最低5元
        
        commission = max(amount * commission_rate, min_commission)
        
        # 印花税（卖出时收取）
        stamp_tax = Decimal('0')
        if order_request.side == OrderSide.SELL:
            stamp_tax = amount * Decimal('0.001')  # 千一
        
        return commission + stamp_tax
    
    def _estimate_execution_time(self, broker_id: str) -> float:
        """估算执行时间"""
        stats = self.broker_stats.get(broker_id, {})
        return stats.get('avg_execution_time', 1.0)  # 默认1秒
    
    def update_broker_stats(self, broker_id: str, execution_time: float, 
                           success: bool, cost: Decimal):
        """更新券商统计"""
        if broker_id not in self.broker_stats:
            self.broker_stats[broker_id] = {
                'total_orders': 0,
                'successful_orders': 0,
                'total_execution_time': 0.0,
                'avg_execution_time': 1.0,
                'success_rate': 1.0,
                'total_cost': Decimal('0'),
                'avg_cost': Decimal('0')
            }
        
        stats = self.broker_stats[broker_id]
        stats['total_orders'] += 1
        stats['total_execution_time'] += execution_time
        stats['total_cost'] += cost
        
        if success:
            stats['successful_orders'] += 1
        
        # 更新平均值
        stats['avg_execution_time'] = stats['total_execution_time'] / stats['total_orders']
        stats['success_rate'] = stats['successful_orders'] / stats['total_orders']
        stats['avg_cost'] = stats['total_cost'] / stats['total_orders']
    
    def get_routing_rules(self) -> List[Dict[str, Any]]:
        """获取路由规则列表"""
        return [
            {
                'symbol_pattern': rule.symbol_pattern,
                'strategy': rule.strategy.value,
                'preferred_brokers': rule.preferred_brokers,
                'max_order_size': rule.max_order_size,
                'min_order_size': rule.min_order_size,
                'split_threshold': rule.split_threshold,
                'priority': rule.priority
            }
            for rule in self.routing_rules
        ]
    
    def get_broker_statistics(self) -> Dict[str, Dict[str, Any]]:
        """获取券商统计信息"""
        return {
            broker_id: {
                'total_orders': stats['total_orders'],
                'successful_orders': stats['successful_orders'],
                'avg_execution_time': stats['avg_execution_time'],
                'success_rate': stats['success_rate'],
                'avg_cost': float(stats['avg_cost'])
            }
            for broker_id, stats in self.broker_stats.items()
        }